/*
 * Copyright 2019-2029 geekidea(https://github.com/geekidea)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.ld.security.auth.shiro.login.jwt.service.impl;

import com.auth0.jwt.interfaces.DecodedJWT;
import com.ld.security.auth.common.sys.entity.SysUserEntity;
import com.ld.security.auth.common.sys.login.UserService;
import com.ld.security.auth.common.sys.po.LoginPo;
import com.ld.security.auth.common.sys.vo.UserInfoVo;
import com.ld.security.auth.shiro.config.ShiroProperties;
import com.ld.security.auth.shiro.login.jwt.JwtProperties;
import com.ld.security.auth.shiro.login.jwt.JwtToken;
import com.ld.security.auth.shiro.login.jwt.service.JwtLoginService;
import com.ld.security.auth.shiro.login.run.LoginRun;
import com.ld.security.auth.shiro.util.JwtUtil;
import com.ld.security.auth.shiro.util.LoginUtil;
import com.ld.security.common.core.exception.ApiException;
import com.ld.security.common.core.util.CrptUtil;
import com.ld.security.common.tomcat.util.RespUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Date;
import java.util.List;

/**
 * <p>
 * 登录服务实现类
 * </p>
 *
 * @author geekidea
 * @date 2019-05-23
 **/

@Slf4j

public class JwtLoginServiceImpl implements JwtLoginService {
    @Autowired
    ShiroProperties shiroProperties;

    @Autowired
    private JwtProperties jwtProperties;

    @Autowired
    UserService userService;

    @Autowired(required = false)
    private List<LoginRun> runList;


    public UserInfoVo getUserInfo(String username) {

        return userService.getUserInfo(username);
    }

    private void setToken(String token){

        //设置新token
        RespUtil.setHeader(jwtProperties.getTokenName(), token);
        RespUtil.writeCookie(jwtProperties.getTokenName(), token);

    }

    @Override
    public String login(LoginPo loginParam) {
        String username = loginParam.getUsername();

        CrptUtil.encrpt(loginParam);


        //清空缓存先
        userService.flushByUsername(username);
        // 从数据库中获取登陆用户信息
        SysUserEntity sysUser = this.getUserInfo(username).getUser();
        if (sysUser == null) {
            log.error("登陆失败,loginParam:{}", loginParam);
            ApiException.throw400("用户不存在");
        }else if(!sysUser.getPassword().equals(loginParam.getPassword())){
            log.error("密码错误,loginParam:{}", loginParam);
            ApiException.throw400("密码错误");
        }
        // 实际项目中，前端传过来的密码应先加密
        // 原始密码：123456
        // 加密规则：sha256(666666+123456) = 751ade2f90ceb660cb2460f12cc6fe08268e628e4607bdb88a00605b3d66973c

        String newSalt;
      /*  if (jwtProperties.isSaltCheck()) {
            // 包装盐值
          //newSalt = jwtProperties.getSecret();
        } else {
            newSalt = jwtProperties.getSecret();
        }*/
              newSalt =jwtProperties.getSecret();


        // 生成token字符串并返回

        String token = JwtUtil.generateToken(username, newSalt, jwtProperties.getExpireSecond());
        log.debug("token:{}", token);

        // 创建AuthenticationToken
        JwtToken jwtToken = JwtToken.build(token, username, newSalt, jwtProperties.getExpireSecond());
        // 从SecurityUtils里边创建一个 subject
        Subject subject = SecurityUtils.getSubject();
        // 执行认证登陆
        subject.login(jwtToken);


        this.setToken(token);

        if(runList != null){
          for (LoginRun loginRun : runList) {
            loginRun.loginSuccess();
          }
        }

        log.debug("登陆成功,username:{}", username);


        return token;
    }

    @Override
    public void refreshToken(JwtToken jwtToken) {
        if (jwtToken == null) {
            return;
        }
        String token = jwtToken.getToken();
        if (StringUtils.isBlank(token)) {
            return;
        }
        // 判断是否刷新token
        boolean isRefreshToken = jwtProperties.isRefreshToken();
        if (!isRefreshToken) {
            return;
        }
        // 获取过期时间
        Date expireDate = JwtUtil.getExpireDate(token);
        // 获取倒计时
        Integer countdown = jwtProperties.getRefreshTokenCountdown();
        // 如果(当前时间+倒计时) > 过期时间，则刷新token
        boolean refresh = DateUtils.addSeconds(new Date(), countdown).after(expireDate);

        if (!refresh) {
            return;
        }

        // 如果token继续发往后台，则提示，此token已失效，请使用新token，不在返回新token，返回状态码：461
        // 如果Redis缓存中没有，JwtToken没有过期，则说明，已经刷新token
//        boolean exists = loginRedisService.exists(token);
//        if (!exists) {
//            throw new AuthenticationException("token已无效，请使用已刷新的token");
//        }
        String username = jwtToken.getUsername();
        // 生成新token
        String newToken = JwtUtil.generateToken(username, jwtToken.getSalt(), jwtProperties.getExpireSecond());
        DecodedJWT decodedJWT = JwtUtil.getJwtInfo(token);
        jwtToken.setToken(newToken)
                .setCreateDate(decodedJWT.getIssuedAt())
                .setExpireDate(decodedJWT.getExpiresAt());



        this.setToken(newToken);

//        // 更新redis缓存
//        LoginSysUserRedisVo loginSysUserRedisVo = loginRedisService.getLoginSysUserRedisVo(username);
//        loginRedisService.cacheLoginInfo(jwtToken, loginSysUserRedisVo, false);
//        log.debug("刷新token成功，原token:{}，新token:{}", jwtToken.getToken(), newToken);
//        // 设置响应头
//        // 刷新token
//        httpServletResponse.setStatus(CommonConstant.JWT_REFRESH_TOKEN_CODE);
//        httpServletResponse.setHeader(JwtTokenUtil.getTokenName(), newToken);
    }

    @Override
    public void logout() {
        Subject subject = SecurityUtils.getSubject();
        //注销
        subject.logout();
        // 获取token

        String username = LoginUtil.getUsername();
        this.setToken("");
        userService.flushByUsername(username);

        log.info("登出成功,username:{},token:{}", username);
    }


    @Override
    public String login() {

        Subject subject = SecurityUtils.getSubject();
        String token = LoginUtil.getToken();

        if(JwtUtil.isExpired(token)){
           ApiException.throw400("令牌已过期，清重新登录");

        }else{
            String username =  JwtUtil.getUsername(token);

            JwtToken jwtToken =   JwtToken.build(token, username, jwtProperties.getSecret(), jwtProperties.getExpireSecond());
            subject.login(jwtToken);

        }
        return token;
    }

    @Override
    public boolean isUse() {
        return !shiroProperties.isOpenCookie();
    }
}
